<?php

namespace App\Models;

use Carbon\Carbon;

class AutoMoneyTransaction extends Model
{
    protected $table = 'auto_money_transactions';

    protected $fillable = [
        'book_id',
        'user_id',
        'category_id',
        'sub_category_id',
        'type',
        'amount',
        'title',
        'description',
        'period_type',
        'period_value',
        'execute_time',
        'start_date',
        'end_date',
        'status',
        'last_executed_date',
        'next_execute_date',
        'executed_count',
    ];

    protected $casts = [
        'amount' => 'decimal:2',
        'start_date' => 'date',
        'end_date' => 'date',
        'last_executed_date' => 'date',
        'next_execute_date' => 'date',
        'execute_time' => 'datetime:H:i:s',
    ];

    // 周期类型常量
    const PERIOD_DAILY = 1;   // 每日
    const PERIOD_WEEKLY = 2;  // 每周
    const PERIOD_MONTHLY = 3; // 每月
    const PERIOD_YEARLY = 4;  // 每年

    // 交易类型常量
    const TYPE_INCOME = 1;    // 收入
    const TYPE_EXPENSE = 2;   // 支出

    // 状态常量
    const STATUS_DISABLED = 0; // 禁用
    const STATUS_ENABLED = 1;  // 启用

    /**
     * 获取该定时记账所属的账本
     */
    public function book()
    {
        return $this->belongsTo(MoneyBook::class, 'book_id');
    }

    /**
     * 获取该定时记账所属的用户
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * 获取该定时记账所属的主分类
     */
    public function category()
    {
        return $this->belongsTo(MoneyCategory::class, 'category_id');
    }

    /**
     * 获取该定时记账所属的子分类
     */
    public function subCategory()
    {
        return $this->belongsTo(MoneySubCategory::class, 'sub_category_id');
    }

    /**
     * 获取周期类型文本
     */
    public function getPeriodTypeTextAttribute()
    {
        $types = [
            self::PERIOD_DAILY => '每日',
            self::PERIOD_WEEKLY => '每周',
            self::PERIOD_MONTHLY => '每月',
            self::PERIOD_YEARLY => '每年',
        ];

        return $types[$this->period_type] ?? '未知';
    }

    /**
     * 获取交易类型文本
     */
    public function getTypeTextAttribute()
    {
        return $this->type == self::TYPE_INCOME ? '收入' : '支出';
    }

    /**
     * 获取状态文本
     */
    public function getStatusTextAttribute()
    {
        return $this->status == self::STATUS_ENABLED ? '启用' : '禁用';
    }

    /**
     * 计算下次执行日期
     */
    public function calculateNextExecuteDate($fromDate = null)
    {
        $fromDate = $fromDate ? Carbon::parse($fromDate) : Carbon::now();

        switch ($this->period_type) {
            case self::PERIOD_DAILY:
                return $fromDate->addDay();

            case self::PERIOD_WEEKLY:
                // period_value 表示周几 (1=周一, 7=周日)
                $targetDayOfWeek = $this->period_value;
                $nextDate = $fromDate->copy();

                // 如果今天就是目标日期且还没执行过，就是今天（新建的时候）
                if ($nextDate->dayOfWeekIso == $targetDayOfWeek
                    && !$this->id
                    && (!$this->last_executed_date || !$this->last_executed_date->isSameDay($nextDate))) {
                    return $nextDate;
                }

                // 否则找下一个目标日期
                while ($nextDate->dayOfWeekIso != $targetDayOfWeek) {
                    $nextDate->addDay();
                }
                return $nextDate;

            case self::PERIOD_MONTHLY:
                // period_value 表示每月第几天
                $targetDay = intval($this->period_value);
                $nextDate = $fromDate->copy();

                // 如果当月目标日期还没到
                if ($nextDate->day <= $targetDay) {
                    $nextDate->day($targetDay);
                    // 如果是今天且还没执行过（新建的时候）
                    if ($nextDate->isSameDay($fromDate)
                        && !$this->id
                        && (!$this->last_executed_date || !$this->last_executed_date->isSameDay($nextDate))) {
                        return $nextDate;
                    }
                    // 如果是未来日期
                    if ($nextDate->gt($fromDate)) {
                        return $nextDate;
                    }
                }

                // 否则是下个月的目标日期
                $nextDate->addMonth()->day($targetDay);
                return $nextDate;

            case self::PERIOD_YEARLY:
                // period_value 表示一年中的某一天，格式为：月份*100+日期，例如：315表示3月15日
                $nextDate = $fromDate->copy();

                if ($this->period_value) {
                    // 解析月份和日期
                    $month = intval($this->period_value / 100);
                    $day = $this->period_value % 100;

                    // 确保月份和日期有效
                    if ($month >= 1 && $month <= 12 && $day >= 1 && $day <= 31) {
                        // 设置为今年的目标日期
                        $targetDate = $nextDate->copy()->month($month)->day($day);

                        // 如果今年的目标日期已过或就是今天但已执行过，则设置为明年的目标日期
                        if ($targetDate->lt($fromDate)
                            || ($targetDate->isSameDay($fromDate)
                                && ($this->id //有id，当前已经执行，需要加一年
                                    || ($this->last_executed_date //没id，当前是创建状态
                                        && $this->last_executed_date->isSameDay($fromDate)
                                    )
                                )
                            )
                        ) {
                            $targetDate->addYear();
                        }

                        return $targetDate;
                    }
                }

                // 如果没有有效的period_value或月份/日期无效，则简单地加一年
                return $nextDate->addYear();

            default:
                return null;
        }
    }

    /**
     * 检查是否需要执行
     */
    public function shouldExecute($date = null)
    {
        $date = $date ? Carbon::parse($date) : Carbon::now();

        // 检查状态
        if ($this->status != self::STATUS_ENABLED) {
            return false;
        }

        // 检查开始日期
        if ($date->lt($this->start_date)) {
            return false;
        }

        // 检查结束日期
        if ($this->end_date && $date->gt($this->end_date)) {
            return false;
        }

        // 检查是否已经执行过
        if ($this->last_executed_date) {
            // 比较完整日期，而不仅仅是日期字符串
            // 确保同一天不会重复执行
            if ($this->last_executed_date->isSameDay($date)) {
                return false;
            }
        }

        // 检查下次执行日期
        if ($this->next_execute_date && $date->lt($this->next_execute_date)) {
            return false;
        }

        return true;
    }

    /**
     * 执行定时记账
     */
    public function execute($executeDate = null)
    {
        $executeDate = $executeDate ? Carbon::parse($executeDate) : Carbon::now();

        if (!$this->shouldExecute($executeDate)) {
            return false;
        }

        // 创建交易记录
        $transaction = MoneyTransaction::create([
            'book_id' => $this->book_id,
            'user_id' => $this->user_id,
            'category_id' => $this->category_id,
            'sub_category_id' => $this->sub_category_id,
            'type' => $this->type,
            'amount' => $this->amount,
            'transaction_date' => $executeDate->toDateString(),
            'description' => $this->description,
            'auto_money_transaction_id' => $this->id, // 设置关联ID
        ]);

        // 更新执行记录
        $this->update([
            'last_executed_date' => $executeDate->format('Y-m-d'),
            'next_execute_date' => $this->calculateNextExecuteDate($executeDate)->format('Y-m-d'),
            'executed_count' => $this->executed_count + 1,
        ]);

        return $transaction;
    }

    /**
     * 获取需要执行的定时记账任务
     */
    public static function getPendingTasks($date = null)
    {
        $date = $date ? Carbon::parse($date) : Carbon::now();

        return static::where('status', self::STATUS_ENABLED)
            ->where('start_date', '<=', $date->format('Y-m-d'))
            ->where(function ($query) use ($date) {
                $query->whereNull('end_date')
                      ->orWhere('end_date', '>=', $date->format('Y-m-d'));
            })
            ->where(function ($query) use ($date) {
                $query->whereNull('next_execute_date')
                      ->orWhere('next_execute_date', '<=', $date->format('Y-m-d'));
            })
            ->get()
            ->filter(function ($autoTransaction) use ($date) {
                return $autoTransaction->shouldExecute($date);
            });
    }
}
